/**
 * CustomBloom 类 - Three.js 的辉光效果管理器
 * 提供了一个完整的辉光效果系统，支持：
 * - 可配置的辉光参数（强度、阈值、半径等）
 * - 自定义辉光颜色
 * - 独立控制物体的辉光效果
 * - 动态启用/禁用辉光系统
 */

// 导入必要的 Three.js 核心和后期处理模块
import * as THREE from 'three'
import { RenderPass } from 'three/examples/jsm/postprocessing/RenderPass.js'
import { UnrealBloomPass } from 'three/examples/jsm/postprocessing/UnrealBloomPass.js'
import { EffectComposer } from 'three/examples/jsm/postprocessing/EffectComposer.js'
import { ShaderPass } from 'three/examples/jsm/postprocessing/ShaderPass.js'
import { OutputPass } from 'three/examples/jsm/postprocessing/OutputPass.js'

/**
 * 默认的辉光效果参数配置
 * @const {Object} DEFAULT_BLOOM_OPTIONS
 * @property {number} threshold - 辉光效果的亮度阈值，只有超过此亮度的像素才会发光
 * @property {number} strength - 辉光效果的强度
 * @property {number} radius - 辉光效果的扩散半径
 * @property {number} exposure - 整体场景的曝光度
 * @property {THREE.Color} bloomColor - 辉光效果的颜色，默认为白色
 */
const DEFAULT_BLOOM_OPTIONS = {
  threshold: 0,
  strength: 1,
  radius: 0.5,
  exposure: 1,
  bloomColor: new THREE.Color(1, 1, 1), // 添加默认的辉光颜色（白色）
}

/**
 * 自定义着色器定义
 * 用于实现辉光效果的颜色混合
 * @const {Object} BLOOM_LAYER_SHADER
 * @property {Object} uniforms - 着色器统一变量
 * @property {string} vertexShader - 顶点着色器代码
 * @property {string} fragmentShader - 片段着色器代码
 */
const BLOOM_LAYER_SHADER = {
  uniforms: {
    baseTexture: { value: null },
    bloomTexture: { value: null },
    bloomColor: { value: new THREE.Color(1, 1, 1) }, // 添加颜色uniform
  },
  vertexShader: `
    varying vec2 vUv;
    void main() {
      vUv = uv;
      gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
    }
  `,
  fragmentShader: `
    uniform sampler2D baseTexture;
    uniform sampler2D bloomTexture;
    uniform vec3 bloomColor;
    varying vec2 vUv;

    void main() {
      vec4 base = texture2D(baseTexture, vUv);
      vec4 bloom = vec4(bloomColor, 1.0) * texture2D(bloomTexture, vUv);
      gl_FragColor = vec4(base.rgb + bloom.rgb, base.a); // 保留 alpha
    }
  `,
}

export default class CustomBloom {
  /**
   * 创建一个新的辉光效果实例
   * @constructor
   * @param {THREE.Scene} scene - Three.js 场景实例
   * @param {THREE.Camera} camera - Three.js 相机实例
   * @param {THREE.WebGLRenderer} renderer - Three.js 渲染器实例
   */
  constructor(scene, camera, renderer) {
    this._scene = scene
    this._camera = camera
    this._renderer = renderer
    this._bloomLayer = new THREE.Layers()
    this._BLOOM_SCENE = 1
    this._darkMaterial = new THREE.MeshBasicMaterial({ color: 'black' })
    this._materials = {}
    this._bloomColor = new THREE.Color(1, 1, 1)

    this._initBloomEffect()
  }

  /**
   * 初始化辉光效果的各个组件
   * 包括：渲染通道、辉光通道、混合通道等
   * @private
   */
  _initBloomEffect() {
    this._bloomLayer.set(this._BLOOM_SCENE)

    const renderScene = new RenderPass(this._scene, this._camera)

    this._bloomPass = new UnrealBloomPass(
      new THREE.Vector2(window.innerWidth, window.innerHeight),
      1.5,
      0.4,
      0.85,
    )

    this._bloomComposer = new EffectComposer(this._renderer)
    this._bloomComposer.renderToScreen = false
    this._bloomComposer.addPass(renderScene)
    this._bloomComposer.addPass(this._bloomPass)

    // 修改混合通道以支持颜色
    this._mixPass = new ShaderPass(
      new THREE.ShaderMaterial({
        uniforms: {
          baseTexture: { value: null },
          bloomTexture: { value: this._bloomComposer.renderTarget2.texture },
          bloomColor: { value: this._bloomColor }, // 添加颜色uniform
        },
        vertexShader: BLOOM_LAYER_SHADER.vertexShader,
        fragmentShader: BLOOM_LAYER_SHADER.fragmentShader,
      }),
      'baseTexture',
    )
    this._mixPass.needsSwap = true

    this._finalComposer = new EffectComposer(this._renderer)
    this._finalComposer.addPass(renderScene)
    this._finalComposer.addPass(this._mixPass)
    this._finalComposer.addPass(new OutputPass())
  }

  /**
   * 更新辉光效果的参数配置
   * @param {Object} options - 辉光参数配置对象
   * @param {number} [options.threshold] - 辉光阈值
   * @param {number} [options.strength] - 辉光强度
   * @param {number} [options.radius] - 辉光半径
   * @param {number} [options.exposure] - 场景曝光度
   * @param {THREE.Color|string|number} [options.bloomColor] - 辉光颜色
   */
  setBloomParameters(options = {}) {
    const params = { ...DEFAULT_BLOOM_OPTIONS, ...options }
    this._bloomPass.threshold = params.threshold
    this._bloomPass.strength = params.strength
    this._bloomPass.radius = params.radius
    this._renderer.toneMappingExposure = Math.pow(params.exposure, 4.0)

    // 设置辉光颜色
    if (params.bloomColor) {
      this.setBloomColor(params.bloomColor)
    }
  }

  /**
   * 设置辉光效果的整体颜色
   * @param {THREE.Color|string|number} color - 新的辉光颜色值
   */
  setBloomColor(color) {
    this._bloomColor = new THREE.Color(color)
    this._mixPass.material.uniforms.bloomColor.value = this._bloomColor
  }

  /**
   * 为特定物体设置独立的辉光颜色，会保存物体的原始颜色
   * @param {THREE.Object3D} object - 目标物体
   * @param {THREE.Color|string|number} color - 辉光颜色值
   */
  setObjectBloomColor(object, color) {
    if (object.material) {
      // 保存原始颜色
      if (!object.userData.originalColor) {
        object.userData.originalColor = object.material.color.clone()
      }
      // 设置新颜色
      object.material.color = new THREE.Color(color)
    }
  }

  /**
   * 将物体的辉光颜色重置为原始颜色
   * @param {THREE.Object3D} object - 要重置的物体
   */
  resetObjectBloomColor(object) {
    if (object.material && object.userData.originalColor) {
      object.material.color = object.userData.originalColor
      delete object.userData.originalColor
    }
  }

  /**
   * 将指定物体添加到辉光渲染层
   * @param {THREE.Object3D} object - 要添加辉光效果的物体
   */
  addToBloomLayer(object) {
    object.layers.enable(this._BLOOM_SCENE)
  }

  /**
   * 从辉光渲染层移除指定物体
   * @param {THREE.Object3D} object - 要移除辉光效果的物体
   */
  removeFromBloomLayer(object) {
    object.layers.disable(this._BLOOM_SCENE)
  }

  /**
   * 切换物体的辉光效果状态
   * @param {THREE.Object3D} object - 要切换辉光状态的物体
   */
  toggleBloom(object) {
    object.layers.toggle(this._BLOOM_SCENE)
  }

  /**
   * 遍历场景，将非辉光层的物体材质变暗
   * @private
   */
  _darkenNonBloomed() {
    const self = this
    this._scene.traverse(function (obj) {
      if (obj.isMesh && !self._bloomLayer.test(obj.layers)) {
        self._materials[obj.uuid] = obj.material
        obj.material = self._darkMaterial
      }
    })
  }

  /**
   * 恢复场景中所有物体的原始材质
   * @private
   */
  _restoreMaterial() {
    const self = this
    this._scene.traverse(function (obj) {
      if (self._materials[obj.uuid]) {
        obj.material = self._materials[obj.uuid]
        delete self._materials[obj.uuid]
      }
    })
  }

  /**
   * 更新辉光效果的渲染尺寸
   * @param {number} width - 新的渲染宽度
   * @param {number} height - 新的渲染高度
   */
  setSize(width, height) {
    this._bloomComposer.setSize(width, height)
    this._finalComposer.setSize(width, height)
  }

  /**
   * 销毁辉光效果，释放资源
   * 清理所有相关的材质、通道和合成器
   */
  dispose() {
    // 清理所有物体的辉光层
    this._scene.traverse((obj) => {
      if (obj.layers) {
        obj.layers.disable(this._BLOOM_SCENE)
      }
    })

    // 恢复所有物体的原始材质
    this._restoreMaterial()

    // 销毁材质
    this._darkMaterial.dispose()

    // 销毁渲染通道
    this._bloomPass.dispose()
    this._mixPass.dispose()

    // 销毁合成器
    this._bloomComposer.dispose()
    this._finalComposer.dispose()

    // 清理引用
    this._bloomPass = null
    this._mixPass = null
    this._bloomComposer = null
    this._finalComposer = null
    this._materials = {}
  }

  /**
   * 临时禁用辉光效果，但保留配置
   */
  disable() {
    this._enabled = false
    // 恢复所有物体的原始材质
    this._restoreMaterial()
  }

  /**
   * 重新启用之前禁用的辉光效果
   */
  enable() {
    this._enabled = true
  }

  /**
   * 执行辉光效果的渲染
   * 包括辉光层的渲染和最终图像的合成
   */
  render() {
    if (!this._enabled) {
      // 如果禁用了辉光效果，直接使用普通渲染
      this._renderer.render(this._scene, this._camera)
      return
    }

    // 渲染辉光部分
    this._darkenNonBloomed()
    this._bloomComposer.render()
    this._restoreMaterial()

    // 渲染最终效果
    this._finalComposer.render()
  }
}
